// SweetAlert
// 2014 (c) - Tristan Edwards
// github.com/t4t5/sweetalert
define([], function() {

	var modalClass = '.sweet-alert',
		    overlayClass = '.sweet-overlay',
		    alertTypes = ['error', 'warning', 'info', 'success'];


		/*
		 * Manipulate DOM
		 */

		var getModal = function() {
		        return document.querySelector(modalClass);
		    },
		    getOverlay = function() {
		        return document.querySelector(overlayClass);
		    },
		    hasClass = function(elem, className) {
		        return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
		    },
		    addClass = function(elem, className) {
		        if (!hasClass(elem, className)) {
		            elem.className += ' ' + className;
		        }
		    },
		    removeClass = function(elem, className) {
		        var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' ';
		        if (hasClass(elem, className)) {
		            while (newClass.indexOf(' ' + className + ' ') >= 0) {
		                newClass = newClass.replace(' ' + className + ' ', ' ');
		            }
		            elem.className = newClass.replace(/^\s+|\s+$/g, '');
		        }
		    },
		    escapeHtml = function(str) {
		        //      var div = document.createElement('div');
		        //      div.appendChild(document.createTextNode(str));
		        //      return div.innerHTML;
		        return str;
		    },
		    _show = function(elem) {
		        elem.style.opacity = '';
		        elem.style.display = 'block';
		    },
		    show = function(elems) {
		        if (elems && !elems.length) {
		            return _show(elems);
		        }
		        for (var i = 0; i < elems.length; ++i) {
		            _show(elems[i]);
		        }
		    },
		    _hide = function(elem) {
		        elem.style.opacity = '';
		        elem.style.display = 'none';
		    },
		    hide = function(elems) {
		        if (elems && !elems.length) {
		            return _hide(elems);
		        }
		        for (var i = 0; i < elems.length; ++i) {
		            _hide(elems[i]);
		        }
		    },
		    isDescendant = function(parent, child) {
		        var node = child.parentNode;
		        while (node !== null) {
		            if (node === parent) {
		                return true;
		            }
		            node = node.parentNode;
		        }
		        return false;
		    },
		    getTopMargin = function(elem) {
		        elem.style.left = '-9999px';
		        elem.style.display = 'block';

		        var height = elem.clientHeight;
		        var padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10);

		        elem.style.left = '';
		        elem.style.display = 'none';
		        return ('-' + parseInt(height / 2 + padding) + 'px');
		    },
		    fadeIn = function(elem, interval) {
		        if (+elem.style.opacity < 1) {
		            interval = interval || 16;
		            elem.style.opacity = 0;
		            elem.style.display = 'block';
		            var last = +new Date();
		            var tick = function() {
		                elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100;
		                last = +new Date();

		                if (+elem.style.opacity < 1) {
		                    setTimeout(tick, interval);
		                }
		            };
		            tick();
		        }
		    },
		    fadeOut = function(elem, interval) {
		        interval = interval || 16;
		        elem.style.opacity = 1;
		        var last = +new Date();
		        var tick = function() {
		            elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100;
		            last = +new Date();

		            if (+elem.style.opacity > 0) {
		                setTimeout(tick, interval);
		            } else {
		                elem.style.display = 'none';
		            }
		        };
		        tick();
		    },
		    fireClick = function(node) {
		        // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
		        // Then fixed for today's Chrome browser.
		        if (MouseEvent) {
		            // Up-to-date approach
		            var mevt = new MouseEvent('click', {
		                view: window,
		                bubbles: false,
		                cancelable: true
		            });
		            node.dispatchEvent(mevt);
		        } else if (document.createEvent) {
		            // Fallback
		            var evt = document.createEvent('MouseEvents');
		            evt.initEvent('click', false, false);
		            node.dispatchEvent(evt);
		        } else if (document.createEventObject) {
		            node.fireEvent('onclick');
		        } else if (typeof node.onclick === 'function') {
		            node.onclick();
		        }
		    },
		    stopEventPropagation = function(e) {
		        // In particular, make sure the space bar doesn't scroll the main window.
		        if (typeof e.stopPropagation === 'function') {
		            e.stopPropagation();
		            e.preventDefault();
		        } else if (window.event && window.event.hasOwnProperty('cancelBubble')) {
		            window.event.cancelBubble = true;
		        }
		    };

		// Remember state in cases where opening and handling a modal will fiddle with it.
		var previousActiveElement,
		    previousDocumentClick,
		    previousWindowKeyDown,
		    lastFocusedButton;

		/*
		 * Add modal + overlay to DOM
		 */

		function initialize() {
		    var sweetHTML = '<div class="sweet-overlay" tabIndex="-1"></div>'+
		                      '<div class="sweet-alert" tabIndex="-1">'+
		                          '<div class="icon error"><span class="x-mark"><span class="line left"></span><span class="line right"></span></span></div>'+
		                          '<div class="icon warning"> <span class="body" style="font-size:10px"></span> <span class="dot"></span> </div>'+
		                          '<div class="icon info"></div>'+
		                          '<div class="icon success"> <span class="line tip"></span> <span class="line long"></span>'+
		                              '<div class="placeholder"></div>'+
		                              '<div class="fix"></div>'+
		                          '</div>'+
		                          '<div class="icon custom"></div>'+
		                          '<h3>Title</h3>'+
		                          '<p>Text</p>'+
		                          '<button class="cancel" tabIndex="2">Cancel</button>'+
		                          '<button class="confirm" tabIndex="1">OK</button>'+
		                      '</div>',
		        sweetWrap = document.createElement('div');

		    sweetWrap.innerHTML = sweetHTML;
		    document.body.appendChild(sweetWrap);
		}



		/*
		 * Global sweetAlert function
		 */

		window.sweetAlert = window.swal = window.FIREFLY = function() {

		    // Default parameters
		    var params = {
		        title: '',
		        text: '',
		        type: null,
		        allowOutsideClick: false,
		        showCancelButton: false,
		        closeOnConfirm: true,
		        confirmButtonText: '确定',
		        confirmButtonColor: '#D1EEEE',
		        cancelButtonText: '取消',
		        imageUrl: null,
		        imageSize: null,
		        callBackFn: null
		    };
		    if (arguments[0] === undefined) {
		        window.console.error('sweetAlert expects at least 1 attribute!');
		        return false;
		    }

		    switch (typeof arguments[0]) {

		        case 'string':
		            params.title = arguments[0];
		            params.text = arguments[1] || '';
		            params.type = arguments[2] || '';
		            params.callBackFn = arguments[3] || null;
		            break;

		        case 'object':
		            if (arguments[0].title === undefined) {
		                window.console.error('Missing "title" argument!');
		                return false;
		            }

		            params.title = arguments[0].title;
		            params.text = arguments[0].text || params.text;
		            params.type = arguments[0].type || params.type;
		            params.allowOutsideClick = arguments[0].allowOutsideClick || params.allowOutsideClick;
		            params.showCancelButton = arguments[0].showCancelButton !== undefined ? arguments[0].showCancelButton : params.showCancelButton;
		            params.closeOnConfirm = arguments[0].closeOnConfirm !== undefined ? arguments[0].closeOnConfirm : params.closeOnConfirm;

		            // Show "Confirm" instead of "OK" if cancel button is visible
		            //        params.confirmButtonText  = (params.showCancelButton) ? 'Confirm' : params.confirmButtonText;

		            params.confirmButtonText = arguments[0].confirmButtonText || params.confirmButtonText;
		            params.confirmButtonColor = arguments[0].confirmButtonColor || params.confirmButtonColor;
		            params.cancelButtonText = arguments[0].cancelButtonText || params.cancelButtonText;
		            params.imageUrl = arguments[0].imageUrl || params.imageUrl;
		            params.imageSize = arguments[0].imageSize || params.imageSize;
		            params.doneFunction = arguments[1] || null;
		            params.cancleFunction = arguments[2] || null;

		            break;

		        default:
		            window.console.error('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]);
		            return false;

		    }

		    //console.log(params.confirmButtonColor);

		    setParameters(params);
		    fixVerticalPosition();
		    openModal();


		    // Modal interactions
		    var modal = getModal();

		    // Mouse interactions
		    var onButtonEvent = function(e) {

		        var target = e.target || e.srcElement,
		            targetedConfirm = (target.className === 'confirm'),
		            modalIsVisible = hasClass(modal, 'visible'),
		            doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true');

		        switch (e.type) {
		            case ("mouseover"):
		                if (targetedConfirm) {
		                    e.target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.04);
		                }
		                break;
		            case ("mouseout"):
		                if (targetedConfirm) {
		                    e.target.style.backgroundColor = params.confirmButtonColor;
		                }
		                break;
		            case ("mousedown"):
		                if (targetedConfirm) {
		                    e.target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.14);
		                }
		                break;
		            case ("mouseup"):
		                if (targetedConfirm) {
		                    e.target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.04);
		                }
		                break;
		            case ("focus"):
		                var $confirmButton = modal.querySelector('button.confirm'),
		                    $cancelButton = modal.querySelector('button.cancel');

		                if (targetedConfirm) {
		                    $cancelButton.style.boxShadow = 'none';
		                } else {
		                    $confirmButton.style.boxShadow = 'none';
		                }
		                break;
		            case ("click"):
		                if (targetedConfirm && doneFunctionExists && modalIsVisible) {
		                    params.doneFunction();
		                    closeModal();
		                    return;
		                } else if (!params.closeOnConfirm) {
		                    params.cancleFunction();
		                    closeModal();
		                    return;
		                } else {
		                    closeModal();
		                }
		                if (params.callBackFn) {
		                    params.callBackFn();
		                }

		                break;
		        }
		    };

		    var $buttons = modal.querySelectorAll('button');
		    for (var i = 0; i < $buttons.length; i++) {
		        $buttons[i].onclick = onButtonEvent;
		        $buttons[i].onmouseover = onButtonEvent;
		        $buttons[i].onmouseout = onButtonEvent;
		        $buttons[i].onmousedown = onButtonEvent;
		        //$buttons[i].onmouseup   = onButtonEvent;
		        $buttons[i].onfocus = onButtonEvent;
		    }

		    // Remember the current document.onclick event.
		    previousDocumentClick = document.onclick;
		    document.onclick = function(e) {
		        var target = e.target || e.srcElement;

		        var clickedOnModal = (modal === target),
		            clickedOnModalChild = isDescendant(modal, e.target),
		            modalIsVisible = hasClass(modal, 'visible'),
		            outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true';

		        if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) {
		            closeModal();
		        }
		    };


		    // Keyboard interactions
		    var $okButton = modal.querySelector('button.confirm'),
		        $cancelButton = modal.querySelector('button.cancel'),
		        $modalButtons = modal.querySelectorAll('button:not([type=hidden])');


		    function handleKeyDown(e) {
		        var keyCode = e.keyCode || e.which;

		        if ([9, 13, 32, 27].indexOf(keyCode) === -1) {
		            // Don't do work on keys we don't care about.
		            return;
		        }

		        var $targetElement = e.target || e.srcElement;

		        var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.
		        for (var i = 0; i < $modalButtons.length; i++) {
		            if ($targetElement === $modalButtons[i]) {
		                btnIndex = i;
		                break;
		            }
		        }

		        if (keyCode === 9) {
		            // TAB
		            if (btnIndex === -1) {
		                // No button focused. Jump to the confirm button.
		                $targetElement = $okButton;
		            } else {
		                // Cycle to the next button
		                if (btnIndex === $modalButtons.length - 1) {
		                    $targetElement = $modalButtons[0];
		                } else {
		                    $targetElement = $modalButtons[btnIndex + 1];
		                }
		            }

		            stopEventPropagation(e);
		            $targetElement.focus();
		            setFocusStyle($targetElement, params.confirmButtonColor); // TODO

		        } else {
		            if (keyCode === 13 || keyCode === 32) {
		                if (btnIndex === -1) {
		                    // ENTER/SPACE clicked outside of a button.
		                    $targetElement = $okButton;
		                } else {
		                    // Do nothing - let the browser handle it.
		                    $targetElement = undefined;
		                }
		            } else if (keyCode === 27 && !($cancelButton.hidden || $cancelButton.style.display === 'none')) {
		                // ESC to cancel only if there's a cancel button displayed (like the alert() window).
		                $targetElement = $cancelButton;
		            } else {
		                // Fallback - let the browser handle it.
		                $targetElement = undefined;
		            }

		            if ($targetElement !== undefined) {
		                fireClick($targetElement, e);
		            }
		        }
		    }

		    previousWindowKeyDown = window.onkeydown;
		    window.onkeydown = handleKeyDown;

		    function handleOnBlur(e) {
		        var $targetElement = e.target || e.srcElement,
		            $focusElement = e.relatedTarget,
		            modalIsVisible = hasClass(modal, 'visible');

		        if (modalIsVisible) {
		            var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.

		            if ($focusElement !== null) {
		                // If we picked something in the DOM to focus to, let's see if it was a button.
		                for (var i = 0; i < $modalButtons.length; i++) {
		                    if ($focusElement === $modalButtons[i]) {
		                        btnIndex = i;
		                        break;
		                    }
		                }

		                if (btnIndex === -1) {
		                    // Something in the dom, but not a visible button. Focus back on the button.
		                    $targetElement.focus();
		                }
		            } else {
		                // Exiting the DOM (e.g. clicked in the URL bar);
		                lastFocusedButton = $targetElement;
		            }
		        }
		    }

		    $okButton.onblur = handleOnBlur;
		    $cancelButton.onblur = handleOnBlur;

		    window.onfocus = function() {
		        // When the user has focused away and focused back from the whole window.
		        window.setTimeout(function() {
		            // Put in a timeout to jump out of the event sequence. Calling focus() in the event
		            // sequence confuses things.
		            if (lastFocusedButton !== undefined) {
		                lastFocusedButton.focus();
		                lastFocusedButton = undefined;
		            }
		        }, 0);
		    };
		};


		/*
		 * Set type, text and actions on modal
		 */

		function setParameters(params) {
		    var modal = getModal();

		    var $title = modal.querySelector('h3'),
		        $text = modal.querySelector('p'),
		        $cancelBtn = modal.querySelector('button.cancel'),
		        $confirmBtn = modal.querySelector('button.confirm');

		    // Title
		    $title.innerHTML = escapeHtml(params.title).split("\n").join("<br>");

		    // Text
		    $text.innerHTML = escapeHtml(params.text || '').split("\n").join("<br>");
		    if (params.text) {
		        show($text);
		    }

		    // Icon
		    hide(modal.querySelectorAll('.icon'));
		    if (params.type) {
		        var validType = false;
		        for (var i = 0; i < alertTypes.length; i++) {
		            if (params.type === alertTypes[i]) {
		                validType = true;
		                break;
		            }
		        }
		        if (!validType) {
		            window.console.error('Unknown alert type: ' + params.type);
		            return false;
		        }
		        var $icon = modal.querySelector('.icon.' + params.type);
		        show($icon);

		        // Animate icon
		        switch (params.type) {
		            case "success":
		                addClass($icon, 'animate');
		                addClass($icon.querySelector('.tip'), 'animateSuccessTip');
		                addClass($icon.querySelector('.long'), 'animateSuccessLong');
		                break;
		            case "error":
		                addClass($icon, 'animateErrorIcon');
		                addClass($icon.querySelector('.x-mark'), 'animateXMark');
		                break;
		            case "warning":
		                addClass($icon, 'pulseWarning');
		                addClass($icon.querySelector('.body'), 'pulseWarningIns');
		                addClass($icon.querySelector('.dot'), 'pulseWarningIns');
		                break;
		        }

		    }

		    // Custom image
		    if (params.imageUrl) {
		        var $customIcon = modal.querySelector('.icon.custom');

		        $customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')';
		        show($customIcon);

		        var _imgWidth = 80,
		            _imgHeight = 80;

		        if (params.imageSize) {
		            var imgWidth = params.imageSize.split('x')[0];
		            var imgHeight = params.imageSize.split('x')[1];

		            if (!imgWidth || !imgHeight) {
		                window.console.error("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize);
		            } else {
		                _imgWidth = imgWidth;
		                _imgHeight = imgHeight;

		                $customIcon.css({
		                    'width': imgWidth + 'px',
		                    'height': imgHeight + 'px'
		                });
		            }
		        }
		        $customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px');
		    }

		    // Cancel button
		    modal.setAttribute('data-has-cancel-button', params.showCancelButton);
		    if (params.showCancelButton) {
		        $cancelBtn.style.display = 'inline-block';
		    } else {
		        hide($cancelBtn);
		    }

		    // Edit text on cancel and confirm buttons
		    if (params.cancelButtonText) {
		        $cancelBtn.innerHTML = escapeHtml(params.cancelButtonText);
		    }
		    if (params.confirmButtonText) {
		        $confirmBtn.innerHTML = escapeHtml(params.confirmButtonText);
		    }

		    // Set confirm button to selected background color
		    $confirmBtn.style.backgroundColor = params.confirmButtonColor;

		    // Set box-shadow to default focused button
		    setFocusStyle($confirmBtn, params.confirmButtonColor);

		    // Allow outside click?
		    modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick);

		    // Done-function
		    var hasDoneFunction = (params.doneFunction) ? true : false;
		    modal.setAttribute('data-has-done-function', hasDoneFunction);
		}


		/*
		 * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color)
		 */

		function colorLuminance(hex, lum) {
		    // Validate hex string
		    hex = String(hex).replace(/[^0-9a-f]/gi, '');
		    if (hex.length < 6) {
		        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
		    }
		    lum = lum || 0;

		    // Convert to decimal and change luminosity
		    var rgb = "#",
		        c, i;
		    for (i = 0; i < 3; i++) {
		        c = parseInt(hex.substr(i * 2, 2), 16);
		        c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
		        rgb += ("00" + c).substr(c.length);
		    }

		    return rgb;
		}

		function hexToRgb(hex) {
		    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
		    return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null;
		}

		// Add box-shadow style to button (depending on its chosen bg-color)
		function setFocusStyle($button, bgColor) {
		    var rgbColor = hexToRgb(bgColor);
		    $button.style.boxShadow = '0 0 2px rgba(' + rgbColor + ', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)';
		}



		/*
		 * Animations
		 */

		function openModal() {
		    var modal = getModal();
		    fadeIn(getOverlay(), 10);
		    show(modal);
		    addClass(modal, 'showSweetAlert');
		    removeClass(modal, 'hideSweetAlert');

		    previousActiveElement = document.activeElement;
		    var $okButton = modal.querySelector('button.confirm');
		    $okButton.focus();

		    setTimeout(function() {
		        addClass(modal, 'visible');
		    }, 100);
		}

		function closeModal() {
		    var modal = getModal();
		    fadeOut(getOverlay(), 5);
		    fadeOut(modal, 5);
		    removeClass(modal, 'showSweetAlert');
		    addClass(modal, 'hideSweetAlert');
		    removeClass(modal, 'visible');


		    // Reset icon animations

		    var $successIcon = modal.querySelector('.icon.success');
		    removeClass($successIcon, 'animate');
		    removeClass($successIcon.querySelector('.tip'), 'animateSuccessTip');
		    removeClass($successIcon.querySelector('.long'), 'animateSuccessLong');

		    var $errorIcon = modal.querySelector('.icon.error');
		    removeClass($errorIcon, 'animateErrorIcon');
		    removeClass($errorIcon.querySelector('.x-mark'), 'animateXMark');

		    var $warningIcon = modal.querySelector('.icon.warning');
		    removeClass($warningIcon, 'pulseWarning');
		    removeClass($warningIcon.querySelector('.body'), 'pulseWarningIns');
		    removeClass($warningIcon.querySelector('.dot'), 'pulseWarningIns');


		    // Reset the page to its previous state
		    window.onkeydown = previousWindowKeyDown;
		    document.onclick = previousDocumentClick;
		    if (previousActiveElement) {
		        previousActiveElement.focus();
		    }
		    lastFocusedButton = undefined;
		}


		/*
		 * Set "margin-top"-property on modal based on its computed height
		 */

		function fixVerticalPosition() {
		    var modal = getModal();

		    modal.style.marginTop = getTopMargin(getModal());
		}



		/*
		 * If library is injected after page has loaded
		 */

		(function() {
		    if (document.readyState === "complete" || document.readyState === "interactive") {
		        initialize();
		    } else {
		        if (document.addEventListener) {
		            document.addEventListener('DOMContentLoaded', function factorial() {
		                document.removeEventListener('DOMContentLoaded', arguments.callee, false);
		                initialize();
		            }, false);
		        } else if (document.attachEvent) {
		            document.attachEvent('onreadystatechange', function() {
		                if (document.readyState === 'complete') {
		                    document.detachEvent('onreadystatechange', arguments.callee);
		                    initialize();
		                }
		            });
		        }
		    }
		})();

});
